package com.yao.dubboprovider.server;

import com.yao.dubboapi.entity.InvokeMessage;
import com.yao.dubboprovider.registry.RegistryCenter;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Henry Yao
 * @since 2021/5/3
 */
public class NettyServerProvider {
    private String address;
    private final List<String> classCache = new ArrayList<>();
    private final Map<String, Class<?>> providerMap = new HashMap<>();

    public void start() {
        NioEventLoopGroup parent = new NioEventLoopGroup();
        NioEventLoopGroup child = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(parent, child)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(InvokeMessage.class.getClassLoader())));
                            pipeline.addLast(new ObjectEncoder());
                            pipeline.addLast(new NettyServerProviderHandler(providerMap));
                        }
                    });
            String host = address.split(":")[0];
            int port = Integer.parseInt(address.split(":")[1]);
            ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync();
            System.out.println("server已启动");
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            parent.shutdownGracefully();
            child.shutdownGracefully();
        }
    }

    public void publish(RegistryCenter registryCenter, String packageName, String address) throws Exception {
        this.address = address;
        // 获取提供的包下所有需要注册的服务
        getProviderClass(packageName);
        // 注册上一步获取到的服务类
        doRegistry(registryCenter);
    }

    private void doRegistry(RegistryCenter registryCenter) throws Exception {
        // 将所有扫描到的类注册到注册中心
        for (String name : classCache) {
            Class<?> clazz = Class.forName(name);
            String serviceName = clazz.getInterfaces()[0].getSimpleName();
            registryCenter.registryService(serviceName, address);
            providerMap.put(serviceName, clazz);
        }
    }

    private void getProviderClass(String packageName) {
        // 将包名中的.转化成/
        URL resource = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
        if (resource == null) {
            System.out.println("资源不存在");
            return;
        }
        File file = new File(resource.getFile());
        for (File f : file.listFiles()) {
            if (f.isDirectory()) {
                getProviderClass(packageName + f.getName());
            } else {
                classCache.add(packageName + "." + f.getName().replace(".class", ""));
            }
        }
    }
}
